---
title: How to create plugins
---

export const metadata = {
  title: "How to create plugins",
  description: "Learn how to create and use stagewise plugins.",
};

# stagewise Plugins

stagewise plugins let you extend the browser toolbar with custom UI, actions, and context for your AI-powered code workflows. Plugins can provide new buttons, panels, and even custom tools that your AI agent can call via MCP (Multi-Context Protocol).

## 1. Getting Started

### Scaffold a Plugin

The fastest way to start is with our CLI:

```bash
npx create-stagewise-plugin
```

This will generate a new plugin project using our template, including all required configuration and example code.

### Project Structure

A typical plugin project looks like this:

```
my-plugin/
  src/
    index.tsx        # Main plugin entry point
    component.tsx    # Example UI component
  build.config.ts
  package.json
  tsconfig.json
  ...
```

## 2. Anatomy of a Plugin

A stagewise plugin is an object that implements the `ToolbarPlugin` interface from `@stagewise/toolbar`. Here's a minimal example:

```tsx
// src/index.tsx
import type { ToolbarPlugin } from '@stagewise/toolbar';
import { ExampleComponent } from './component';

const ExamplePlugin: ToolbarPlugin = {
  displayName: 'Example',
  description: 'Example Plugin',
  iconSvg: null, // You can provide a VNode for a custom SVG icon
  pluginName: 'example',
  onActionClick: () => <ExampleComponent />,
};

// It's super important to default-export the plugin!
export default ExamplePlugin;
```

### Key Fields
- `displayName`: Shown to users in the toolbar
- `pluginName`: Internal unique name
- `description`: Short summary
- `iconSvg`: (optional) Monochrome SVG icon as a Preact VNode
- `onActionClick`: (optional) Returns a VNode to render when the plugin is activated

See the [full interface in the codebase](https://github.com/stagewise-io/stagewise/blob/main/toolbar/core/src/plugin.ts) for advanced hooks and MCP integration.

## 3. Building Plugin UI with `@plugin-ui`

stagewise provides a set of UI primitives for building plugin panels:

### Available Components
- `Panel`: Card-like container with optional `Header`, `Content`, and `Footer`
- `Button`: Styled button with variants
- `Badge`: Colored label for status/info

#### Example Usage
```tsx
// src/component.tsx
import { Panel, PanelHeader, PanelContent, Button, useToolbar } from '@stagewise/toolbar/plugin-ui';

export const ExampleComponent = () => {
  const toolbar = useToolbar();

  return (
    <Panel>
      <PanelHeader title="Example Plugin" />
      <PanelContent>
        <Button onClick={() => toolbar.sendPrompt('Hello world!')}>
          Send "Hello world!" to Cursor!
        </Button>
      </PanelContent>
    </Panel>
  );
};
```

### The `useToolbar` Hook
This hook gives you access to the toolbar context, including the `sendPrompt` method to send messages to your AI agent or the window object of the main web app.

## 4. Advanced Plugin Features

### Context Snippets & Annotations
Plugins can provide extra context for prompts or annotate selected elements. Implement these hooks in your plugin:

- `onPromptSend`: Add context before a prompt is sent
- `onContextElementSelect`: Annotate selected DOM elements

### MCP Integration
Expose custom tools, resources, or prompts to your AI agent by implementing the `mcp` property. See the `ToolbarPlugin` interface for details.

## 5. Development & Build

- Use `pnpm dev` to start development
- Use `pnpm build` to produce a distributable package

There are two main approaches to test your plugin during development, depending on your project structure:

### Option A: Using workspace:* in a Monorepo

If your plugin is part of a monorepo setup, the simplest approach is to use workspace dependencies:

1. In your test project's `package.json`, add your plugin as a workspace dependency:
```json
{
  "dependencies": {
    "@stagewise/plugin-example": "workspace:*"
  }
}
```

2. Run `pnpm install` to set up the workspace dependency

This approach automatically makes your plugin available to other packages in your workspace, and changes will be reflected immediately after rebuilding the plugin.

### Option B: Using Local NPM Linking with pnpm

If you're developing your plugin outside of a monorepo, you can use pnpm's linking feature to test it locally:

#### Step-by-step: Local Linking with pnpm

1. Build your plugin (from the plugin directory):
```bash
pnpm build
```

2. Link your plugin locally:
```bash
pnpm link --global
```

3. Link the plugin in your example project:
Navigate to your example app (e.g., `examples/next-example/`):
```bash
pnpm link --global @stagewise/plugin-example
```

4. Use your plugin in the example app:
Import and add your plugin to the toolbar config as you would with a published package.

5. Develop and test:
- Changes to your plugin source will be reflected after you rebuild (`pnpm build`) in the plugin directory
- Restart your example app's dev server if needed

> **Tip:** When you're done with local linking, you can unlink with `pnpm unlink --global @stagewise/plugin-example` in your example app and `pnpm unlink --global` in your plugin directory.

Once your plugin is ready, you can publish it to npm or continue using it locally with either of the above approaches.

## 6. Full Example

The plugin:
```tsx
// src/index.tsx
import type { ToolbarPlugin } from '@stagewise/toolbar';
import { ExampleComponent } from './component';

export const ExamplePlugin: ToolbarPlugin = {
  displayName: 'Example',
  description: 'Example Plugin',
  iconSvg: null,
  pluginName: 'example',
  onActionClick: () => <ExampleComponent />,
};

// src/component.tsx
import { Panel, PanelHeader, PanelContent, Button, useToolbar } from '@stagewise/toolbar/plugin-ui';

export const ExampleComponent = () => {
  const toolbar = useToolbar();
  return (
    <Panel>
      <PanelHeader title="Example Plugin" />
      <PanelContent>
        <Button onClick={() => toolbar.sendPrompt('Hello world!')}>
          Send "Hello world!" to Cursor!
        </Button>
      </PanelContent>
    </Panel>
  );
};
```

Using the plugin in your next-app:

```tsx
// app/layout.tsx
import type { Metadata } from 'next';
import { StagewiseToolbar } from '@stagewise/toolbar-next';
import { ExamplePlugin } from '@stagewise/plugin-example'; // <-- replace this with your plugin-name

export const metadata: Metadata = {
  title: 'My Next.js App',
  description: 'An app using stagewise plugins',
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <StagewiseToolbar config={{ plugins: [ExamplePlugin] }} />
        {children}
      </body>
    </html>
  );
}
```

---

For more advanced examples and API details, see the [stagewise repository](https://github.com/stagewise-io/stagewise) and the [ToolbarPlugin interface](https://github.com/stagewise-io/stagewise/blob/main/toolbar/core/src/plugin.ts). 